package com.newly.center.sys.service.menu.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.newly.center.sys.service.authEntity.api.IAuthEntityService;
import com.newly.center.sys.service.menu.api.IMenuService;
import com.newly.center.sys.service.tenant.api.ITenantService;
import com.newly.common.base.constants.AuthConstant;
import com.newly.common.base.constants.MenuConstant;
import com.newly.common.base.entity.auth.ICurrentTenant;
import com.newly.common.base.entity.sys.menu.dto.MenuDto;
import com.newly.common.base.entity.sys.menu.dto.MenuMetaDto;
import com.newly.common.base.entity.sys.menu.po.MenuMetaPo;
import com.newly.common.base.entity.sys.menu.po.MenuPo;
import com.newly.common.base.entity.sys.menu.vo.MenuAbilityVo;
import com.newly.common.base.entity.sys.menu.vo.MenuRenderRuleVo;
import com.newly.common.base.entity.sys.menu.xml.MenuItemXml;
import com.newly.common.base.entity.sys.menu.xml.MenuItemsXml;
import com.newly.common.base.entity.sys.tenant.bo.SimpleTenantBo;
import com.newly.common.base.exception.NewlyException;
import com.newly.common.core.utils.ConverseUtils;
import com.newly.common.core.utils.RequestUtils;
import com.newly.transfer.redis.RedisTransfer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author gjm guan
 * @date 2021/9/27 16:26
 * @desc 菜单管理
 */
@Service
@Slf4j
public class MenuService extends AbstractMenuService implements IMenuService {

    @Autowired
    private RequestUtils requestUtils;

    @Autowired
    private RedisTransfer redisTransfer;

    @Autowired
    private ITenantService tenantService;

    @Autowired
    private IAuthEntityService authEntityService;

    @Override
    public Integer save(MenuDto menuDto) {
        Assert.notNull(menuDto, "Menu data can't be empty!");
        Assert.notNull(menuDto.getType(), "Type can't be empty!");
        Assert.notNull(menuDto.getAbility(), "Ability can't be empty!");

        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        currentTenant.assertIsDev();

        // 校验父菜单是否符合规则
        checkRule(menuDto);

        menuDto.setCreateBy(currentTenant.getUserId());
        Integer menuId = this.insert(menuDto);
        Assert.notNull(menuId, "Failed to create menu!");

        boolean result = authEntityService.bind(AuthConstant.AuthEntityType.MENU,
                currentTenant.getSelfRoleId(), menuId.toString());
        Assert.isTrue(result, "Menu binding permission failed!");

        // 刷新菜单
        refreshCache();

        return menuId;
    }

    @Override
    public boolean saveAll(List<MenuDto> menuDtos) {
        Assert.notEmpty(menuDtos, "No menu datum!");

        for (MenuDto menuDto : menuDtos) {
            log.error("create parent menu: {}", menuDto);
            Integer menuId = save(menuDto);
            Assert.notNull(menuId, "Failed to create menu!");
            List<MenuDto> childrenMenuDto = menuDto.getChildren();
            if (!ObjectUtil.isEmpty(childrenMenuDto)) {
                saveChildren(menuId, childrenMenuDto);
            }
        }
        return true;
    }

    @Override
    public void saveChildren(Integer parentMenuId, List<MenuDto> childrenMenuDto) {
        if (ObjectUtil.isEmpty(parentMenuId) || ObjectUtil.isEmpty(childrenMenuDto))
            return;
        for (MenuDto menuDto : childrenMenuDto) {
            log.error("create child menu: {}", menuDto);
            menuDto.setParentId(parentMenuId);
            Integer menuId = save(menuDto);
            Assert.notNull(menuId, "Failed to create child menu!");
            saveChildren(menuId, menuDto.getChildren());
        }
    }

    @Override
    public boolean refreshCache() {
        List<MenuPo> menuPos = this.getAll();
        //Assert.notEmpty(menuPos, "No menu datum!");
        if (ObjectUtil.isEmpty(menuPos)) {
            log.debug("The menu data is null");
            return false;
        }

        List<MenuDto> menuDtos = BeanUtil.copyToList(menuPos, MenuDto.class);
        boolean result = redisTransfer.setMenus(menuDtos);
        Assert.isTrue(result, "Cache menu to redis error!");

        return result;
    }

    @Override
    public boolean modifySortById(Integer menuId, Integer sort) {
        MenuDto menuDto = new MenuDto();
        menuDto.setId(menuId);
        menuDto.setSort(sort);
        return this.modifyById(menuDto);
    }

    @Override
    public boolean modifyEnableById(List<Integer> menuIds, Integer enable) {
        if (ObjectUtil.isEmpty(menuIds)) return false;
        // 为了避免出现脏数据，不允许乱修改菜单值
        assertEnable(enable);
        LambdaQueryWrapper<MenuPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(MenuPo::getId, menuIds);
        MenuPo menuPo = new MenuPo();
        menuPo.setEnable(enable);
        int update = menuMapper.update(menuPo, wrapper);
        return update > 0;
    }


    @Override
    public boolean modifyAccessById(List<Integer> menuIds, Integer access) {
        if (ObjectUtil.isEmpty(menuIds)) return false;
        assertAccess(access);
        LambdaQueryWrapper<MenuPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(MenuPo::getId, menuIds);
        MenuPo menuPo = new MenuPo();
        menuPo.setAccess(access);
        int update = menuMapper.update(menuPo, wrapper);
        return update > 0;
    }


    @Override
    public boolean modifyLinkageProperties(MenuDto menuDto) {
        // 需要父子联动的属性
        MenuPo menuPo = BeanUtil.copyProperties(menuDto, MenuPo.class);
        Integer enable = menuDto.getEnable();
        if (!ObjectUtil.isEmpty(enable)) {
            List<Integer> childrenMenuIds = parseIds(getChildren(Arrays.asList(menuPo)));
            modifyEnableById(childrenMenuIds, enable);
        }
        Integer access = menuDto.getAccess();
        if (!ObjectUtil.isEmpty(access)) {
            List<Integer> childrenMenuIds = parseIds(getChildren(Arrays.asList(menuPo)));
            modifyAccessById(childrenMenuIds, access);
        }
        return true;
    }

    @Override
    public boolean modifyById(MenuDto menuDto) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        currentTenant.assertIsDev();

        MenuPo menuPo = this.getById(menuDto.getId());
        Assert.notNull(menuPo, "Not found menu!");

        //checkRule(menuDto);
        boolean result = this.modifyLinkageProperties(menuDto);
        Assert.isTrue(result, "Failed to modify linkage property of menu!");

        // 修改菜单
        result = this.updateById(menuDto) > 0;
        Assert.isTrue(result, "Failed to modify menu error!");

        // 修改菜单元数据
        if (!ObjectUtil.isEmpty(menuDto.getMenuMeta())) {
            MenuMetaDto menuMeta = menuDto.getMenuMeta();
            menuMeta.setId(menuPo.getMetaId());
            result = this.modifyMeta(menuDto.getMenuMeta());
            Assert.isTrue(result, "Failed to modify meta of menu!");
        }
        return refreshCache();

    }

    @Override
    public boolean modifyMeta(MenuMetaDto menuMetaDto) {
        if (ObjectUtil.isEmpty(menuMetaDto)) return false;
        MenuMetaPo menuMetaPo = BeanUtil.copyProperties(menuMetaDto, MenuMetaPo.class);
        int update = menuMetaMapper.updateById(menuMetaPo);
        return update > 0;
    }

    @Transactional
    @Override
    public boolean removeByIds(List<Integer> menuIds) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        currentTenant.assertIsDev();

        List<MenuPo> menuPos = this.getByIds(menuIds);
        Assert.notEmpty(menuPos, "Menu datum not found!");

        // 递归获取所有子菜单
        List<MenuPo> childrenMenuPo = this.getChildren(menuPos);
        if (!ObjectUtil.isEmpty(childrenMenuPo)) {
            menuPos.addAll(childrenMenuPo);
        }

        menuIds = this.parseIds(menuPos);
        List<Integer> menuMetaIds = childrenMenuPo.stream().map(menuPo -> menuPo.getMetaId())
                .collect(Collectors.toList());
        // 删除菜单
        int result = this.deleteByIds(menuIds);
        // 删除菜单元数据
        result = this.deleteMenuMetaByIds(menuMetaIds);
        // 删除权限实体数据
        Integer selfRoleId = currentTenant.getSelfRoleId();

        authEntityService.unBind(AuthConstant.AuthEntityType.MENU,
                selfRoleId, ConverseUtils.toString(menuIds));

        return refreshCache();
    }

    @Override
    public List<Long> parseCreateTenantIds(List<MenuPo> menuPos) {
        if (ObjectUtil.isEmpty(menuPos)) return null;

        return menuPos.stream().map(menuPo -> menuPo.getCreateBy())
                .collect(Collectors.toSet())
                .stream().collect(Collectors.toList());
    }

    @Override
    public List<Integer> parseIds(List<MenuPo> menuPos) {
        if (ObjectUtil.isEmpty(menuPos)) return null;

        return menuPos.stream().map(menuPo -> menuPo.getId())
                .collect(Collectors.toList());
    }

    @Override
    public List<MenuPo> list(Integer offset, Integer limit, Integer abilityCode,
                             Integer typeCode, Long createBy, String keyword) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        currentTenant.assertIsDev();

        LambdaQueryWrapper<MenuPo> wrapper = new LambdaQueryWrapper<>();
        // 只查询顶级菜单
        wrapper.orderByDesc(MenuPo::getSort);
        wrapper.orderByDesc(MenuPo::getCreateTime);
        // 菜单能力
        if (ObjectUtil.isNotEmpty(abilityCode)) {
            wrapper.eq(MenuPo::getAbility, abilityCode);
        }
        // 菜单类型
        if (ObjectUtil.isNotEmpty(typeCode)) {
            wrapper.eq(MenuPo::getType, typeCode);
        }
        // 创建人
        if (ObjectUtil.isNotEmpty(createBy)) {
            wrapper.eq(MenuPo::getCreateBy, createBy);
        }
        // 关键字
        if (StrUtil.isNotBlank(keyword)) {
            wrapper.like(MenuPo::getName, keyword);
        }

        List<MenuPo> menuPos = menuMapper.selectList(wrapper);
        if (ObjectUtil.isEmpty(menuPos)) return null;

        List<Integer> tempMenuIds = new ArrayList<>(menuPos.size());
        for (MenuPo menuPo : menuPos) {
            menuPo.setMenuMeta(selectMetaById(menuPo.getMetaId()));
            setProperties(menuPo);
            tempMenuIds.add(menuPo.getId());
        }

        return packageSourceChildren(menuPos);
    }

    @Override
    public void packageDBChildren(List<MenuPo> menuPos) {
        if (ObjectUtil.isEmpty(menuPos)) return;

        Collections.sort(menuPos);
        for (MenuPo menuPo : menuPos) {
            setProperties(menuPo);
            menuPo.setChildren(this.selectPackageChildren(menuPo));
        }
    }

    @Override
    public List<MenuPo> packageSourceChildren(List<MenuPo> menuPos) {
        if (ObjectUtil.isEmpty(menuPos)) return null;
        // 先遍历过滤出根菜单，递归封装子菜单
        List<Integer> tempMenuIds = this.parseIds(menuPos);
        return menuPos.stream().filter(menuPo -> !tempMenuIds.contains(menuPo.getParentId()))
                .map(menuPo -> {
                    menuPo.setChildren(packageSourceChildren(menuPo, menuPos));
                    return menuPo;
                }).collect(Collectors.toList());
    }

    @Override
    public List<MenuPo> packageSourceChildren(MenuPo parentMenuPo, List<MenuPo> childrenMenuPo) {
        if (ObjectUtil.isEmpty(parentMenuPo)) return null;

        List<MenuPo> packageMenus = new ArrayList<>();
        for (MenuPo menuPo : childrenMenuPo) {
            if (menuPo.getParentId().equals(parentMenuPo.getId())) {
                packageMenus.add(menuPo);
                packageSourceChildren(menuPo, childrenMenuPo);
            }
        }
        parentMenuPo.setChildren(packageMenus);
        return packageMenus;
    }

    @Override
    public List<MenuPo> getChildren(List<MenuPo> parentMenuPo) {
        if (ObjectUtil.isEmpty(parentMenuPo)) return null;

        List<MenuPo> allMenuPo = new ArrayList<>();
        for (MenuPo menuPo : parentMenuPo) {
            this.selectChildren(menuPo, allMenuPo);
        }
        return allMenuPo;
    }


    @Override
    public List<MenuPo> getAll() {
        List<MenuPo> menuPos = this.selectAll();
        if (!ObjectUtil.isEmpty(menuPos)) {
            for (MenuPo menuPo : menuPos) {
                setProperties(menuPo);
            }
        }
        return menuPos;
    }

    @Override
    public List<MenuPo> getByIds(List<Integer> menuIds) {
        List<MenuPo> menuPos = this.selectByIds(menuIds);
        if (!ObjectUtil.isEmpty(menuPos)) {
            for (MenuPo menuPo : menuPos) {
                setProperties(menuPo);
            }
        }
        return menuPos;
    }

    @Override
    public MenuPo getById(Integer menuId) {
        List<MenuPo> menuPos = getByIds(Arrays.asList(menuId));
        if (!ObjectUtil.isEmpty(menuPos)) {
            return menuPos.get(0);
        }
        return null;
    }

    @Override
    public List<String> getAuthByIds(List<Integer> menuIds) {
        List<MenuPo> menuPos = this.getByIds(menuIds);
        Set<String> menuAuths = new HashSet<>();
        if (!ObjectUtil.isEmpty(menuPos)) {
            for (MenuPo menuPo : menuPos) {
                String auth = menuPo.getAuth();
                if (StrUtil.isBlank(auth))
                    continue;
                String[] splitAuths = auth.split(",");
                menuAuths.addAll(Arrays.asList(splitAuths));
            }
        }
        return menuAuths.stream().collect(Collectors.toList());
    }

    @Override
    public List<MenuPo> getInAuth() {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();

        List<Integer> menuIds = currentTenant.getMenuIds();
        Assert.notEmpty(menuIds, "No menu datum!");

        // redis中尝试获取
        List<MenuPo> menuPos = this.getByIdsInCache(menuIds);
        // 数据库中获取
        if (ObjectUtil.isEmpty(menuPos)) {
            menuPos = this.getByIds(menuIds);
        }

        // 过滤掉所有关闭的菜单，排序菜单
        if (!ObjectUtil.isEmpty(menuPos)) {
            menuPos = menuPos.stream().filter(menuPo ->
                    menuPo.getEnable().equals(MenuConstant.Enable.OPEN.getCode()))
                    .map(menuPo -> {
                        setProperties(menuPo);
                        return menuPo;
                    }).sorted((m1, m2) -> m2.getSort() - m1.getSort()).collect(Collectors.toList());
        }

        return menuPos;
    }

    @Override
    public MenuPo getByIdInAuth(Integer menuId) {
        List<MenuPo> menuPos = getByIdsInAuth(Arrays.asList(menuId));
        if (!ObjectUtil.isEmpty(menuPos)) {
            return menuPos.get(0);
        }
        return null;
    }

    @Override
    public List<MenuPo> getByIdsInAuth(List<Integer> menuIds) {
        if (ObjectUtil.isEmpty(menuIds)) return null;

        List<MenuPo> allMenu = this.getInAuth();
        if (ObjectUtil.isEmpty(allMenu)) return null;

        List<MenuPo> menuPos = new ArrayList<>(menuIds.size());
        for (Integer menuId : menuIds) {
            for (MenuPo menu : allMenu) {
                if (menu.getId().equals(menuId)) {
                    menuPos.add(menu);
                    continue;
                }
            }
        }
        return menuPos;
    }

    @Override
    public List<MenuPo> getByParentIdInAuth(Integer menuId) {
        return getByParentIdsInAuth(Arrays.asList(menuId));
    }

    @Override
    public List<MenuPo> getByParentIdsInAuth(List<Integer> menuIds) {
        if (ObjectUtil.isEmpty(menuIds)) return null;

        List<MenuPo> allMenu = this.getInAuth();
        if (ObjectUtil.isEmpty(allMenu)) return null;

        return allMenu.stream().map(menuPo -> {
            for (Integer menuId : menuIds) {
                if (menuId.equals(menuPo.getParentId())) {
                    return menuPo;
                }
            }
            return null;
        }).filter(menuPo -> menuPo != null).collect(Collectors.toList());
    }

    @Override
    public List<MenuPo> getByIdsInCache(List<Integer> menuIds) {
        if (ObjectUtil.isEmpty(menuIds)) return null;

        List<MenuDto> menuDtos = redisTransfer.getMenus();
        if (ObjectUtil.isEmpty(menuDtos)) return null;

        List<MenuPo> menuPos = new ArrayList<>(menuIds.size());
        for (Integer menuId : menuIds) {
            for (MenuDto menuDto : menuDtos) {
                if (menuDto.getId().equals(menuId)) {
                    menuPos.add(BeanUtil.copyProperties(menuDto, MenuPo.class));
                    continue;
                }
            }
        }
        return menuPos;
    }


    @Override
    public List<SimpleTenantBo> getCreateTenant() {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        currentTenant.assertIsDev();

        // 得到所有菜单的创建人id，已去重
        List<Long> tenantIds = this.parseCreateTenantIds(getAll());
        if (ObjectUtil.isEmpty(tenantIds)) return null;

        List<SimpleTenantBo> simpleTenantBos = new ArrayList<>();
        for (Long tenantId : tenantIds) {
            SimpleTenantBo simpleTenant = tenantService.getSimpleTenant(tenantId);
            if (ObjectUtil.isNotEmpty(simpleTenant)) {
                simpleTenantBos.add(simpleTenant);
            }
        }
        return simpleTenantBos;
    }

    @Override
    public List<MenuPo> chooseLayout(List<MenuPo> menuPos) {
        if (ObjectUtil.isEmpty(menuPos)) return null;
        return menuPos.stream().map(menuPo -> {
            if (isLayoutMenu(menuPo.getType())) {
                return menuPo;
            }
            return null;
        }).filter(menuPo -> menuPo != null).collect(Collectors.toList());
    }

    @Override
    public List<MenuPo> choosePermList(List<MenuPo> menuPos) {
        if (ObjectUtil.isEmpty(menuPos)) return null;
        return menuPos.stream().map(menuPo -> {
            if (isPermListMenu(menuPo.getType())) {
                return menuPo;
            }
            return null;
        }).filter(menuPo -> menuPo != null).collect(Collectors.toList());
    }

    @Override
    public List<MenuPo> getLayoutInAuth() {
        List<MenuPo> menuPos = this.getInAuth();
        List<MenuPo> layoutMenuPos = chooseLayout(menuPos);
        //this.packageLayoutMenu(layoutMenuPos);
        return layoutMenuPos;
    }

    @Override
    public List<MenuPo> getPermListInAuth() {
        List<MenuPo> menuPos = this.choosePermList(getInAuth());
        return this.packageSourceChildren(menuPos);
    }

    @Override
    public void packageLayoutMenu(List<MenuPo> layoutMenuPos) {
        if (ObjectUtil.isEmpty(layoutMenuPos)) return;
        for (MenuPo layoutMenuPo : layoutMenuPos) {
            if (layoutMenuPo.getType().equals(MenuConstant.Type.DIR.getCode())
                    && !layoutMenuPo.getParentId().equals(MenuConstant.MENU_PARENT_FLAG)) {
                for (MenuPo menuPo : layoutMenuPos) {
                    if (menuPo.getType().equals(MenuConstant.Type.MENU.getCode())
                            && menuPo.getParentId().equals(layoutMenuPo.getId())) {
                        layoutMenuPo.setClientPath(menuPo.getClientPath());
                        break;
                    }
                }
            }
        }
    }

    @Override
    public List<MenuPo> getPageInnerInAuth(Integer menuId) {
        return getPageInnerInAuth(Arrays.asList(menuId));
    }

    @Override
    public List<MenuPo> getPageInnerInAuth(List<Integer> menuIds) {
        List<MenuPo> menuPos = this.getByIdsInAuth(menuIds);
        Assert.notEmpty(menuPos, "No menu datum!");
        for (MenuPo menuPo : menuPos) {
            Assert.isTrue(menuPo.getType().equals(MenuConstant.Type.MENU.getCode()),
                    "The menu type is illegal!");
        }
        // 获取子菜单
        return getByParentIdsInAuth(menuIds);
    }

    @Override
    public Map<Integer, Map<String, MenuAbilityVo>> listMenuAbilityMapInAuth() {
        List<MenuPo> menuPos = this.getByTypeInAuth(MenuConstant.Type.MENU);
        if (ObjectUtil.isEmpty(menuPos)) return null;

        Map<Integer, Map<String, MenuAbilityVo>> abilityMap = new HashMap<>();
        for (MenuPo menuPo : menuPos) {
            Map<String, MenuAbilityVo> abilities = new HashMap<>();
            abilityMap.put(menuPo.getId(), abilities);

            MenuConstant.Ability[] values = MenuConstant.Ability.values();

            // 获取当前页面内菜单（按钮）
            List<MenuPo> btnMenuPos = getPageInnerInAuth(menuPo.getId());
            for (MenuConstant.Ability value : values) {
                boolean flag = false;
                if (!ObjectUtil.isEmpty(btnMenuPos)) {
                    for (MenuPo btnMenu : btnMenuPos) {
                        if (btnMenu.getAbility().equals(value.getCode())) {
                            flag = true;
                            break;
                        }
                    }
                }
                value.setFlag(flag);
                MenuAbilityVo menuAbilityVo = new MenuAbilityVo(value);
                abilities.put(value.getName(), menuAbilityVo);
            }
        }
        return abilityMap;
    }

    @Override
    public MenuItemsXml getExportDatum(List<Integer> menuIds) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        currentTenant.assertIsDev();

        List<MenuPo> menuPos = null;
        if (!ObjectUtil.isEmpty(menuIds)) {
            menuPos = this.getByIds(menuIds);
            // 不允许单独导出子菜单
            for (MenuPo menuPo : menuPos) {
                if (menuPo.getParentId() != 0) {
                    throw new NewlyException("Parent id of menu must equal 0!");
                }
            }
        } else {
            menuPos = getAll();
        }
        Assert.notEmpty(menuPos, "No menu datum!");

        // 转换格式
        List<MenuPo> treeMenuPos = this.packageSourceChildren(menuPos);
        List<MenuItemXml> menuItemXmls = BeanUtil.copyToList(treeMenuPos, MenuItemXml.class);
        MenuItemsXml menuItemsXml = new MenuItemsXml();
        menuItemsXml.setMenu(menuItemXmls);
        return menuItemsXml;
    }

    @Override
    public MenuItemsXml getImportDatum(MultipartFile multipartFile) {
        requestUtils.getCurrentTenant().assertIsDev();
        MenuItemsXml menuItemsXml = null;
        try {
            InputStream xmlIs = multipartFile.getInputStream();
            String xml = inputStreamToString(xmlIs);
            XmlMapper xmlMapper = new XmlMapper();
            xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            menuItemsXml = xmlMapper.readValue(xml, MenuItemsXml.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return menuItemsXml;
    }

    @Override
    public List<MenuPo> getParentByIds(List<Integer> childMenuIds) {
        List<MenuPo> menuPos = this.getByIds(childMenuIds);
        if (ObjectUtil.isEmpty(menuPos)) return null;

        List<Integer> parentIds = new ArrayList<>(menuPos.size());
        for (MenuPo menuPo : menuPos) {
            Integer parentId = menuPo.getParentId();
            if (!ObjectUtil.isEmpty(parentId)) {
                parentIds.add(parentId);
            }
        }
        return this.getByIds(parentIds);
    }

    @Override
    public MenuPo getParentById(Integer childMenuId) {
        List<MenuPo> parentMenuPos = getParentByIds(Arrays.asList(childMenuId));
        if (!ObjectUtil.isEmpty(parentMenuPos)) {
            return parentMenuPos.get(0);
        }
        return null;
    }

    @Override
    public List<MenuPo> getByParentIds(List<Integer> parentMenuIds) {
        return this.getByIds(parentMenuIds);
    }

    @Override
    public MenuPo getByParentId(Integer parentMenuId) {
        List<MenuPo> menuPos = getByParentIds(Arrays.asList(parentMenuId));
        if (!ObjectUtil.isEmpty(menuPos)) {
            return menuPos.get(0);
        }
        return null;
    }

    @Override
    public MenuRenderRuleVo getRenderRule(Integer typeCode) {
        if (ObjectUtil.isEmpty(typeCode)) return null;

        MenuRenderRuleVo menuRenderRuleVo = new MenuRenderRuleVo();
        menuRenderRuleVo.setName(true);
        menuRenderRuleVo.setParentId(true);
        menuRenderRuleVo.setEnable(true);
        menuRenderRuleVo.setRemark(true);
        menuRenderRuleVo.setSort(true);
        menuRenderRuleVo.setAccess(true);
        menuRenderRuleVo.setType(true);
        menuRenderRuleVo.setAbility(true);
        menuRenderRuleVo.setApiPerm(true);
        //menuRenderRuleVo.setIcon(true);

        // 菜单
        if (MenuConstant.Type.MENU.getCode() == typeCode) {
            menuRenderRuleVo.setAuth(true);
            menuRenderRuleVo.setClientUri(true);
            menuRenderRuleVo.setClientPath(true);
            menuRenderRuleVo.setIcon(true);
        }

        // 按钮
        if (MenuConstant.Type.BTN.getCode() == typeCode) {
            menuRenderRuleVo.setAuth(true);
            menuRenderRuleVo.setServerUri(true);
        }

        // 系统
        if (MenuConstant.Type.SYS.getCode() == typeCode) {
            menuRenderRuleVo.setServerUri(true);
            menuRenderRuleVo.setAuth(true);
        }

        // 目录
        if (MenuConstant.Type.DIR.getCode() == typeCode) {
            menuRenderRuleVo.setClientUri(true);
            menuRenderRuleVo.setClientPath(true);
            menuRenderRuleVo.setIcon(true);
        }
        return menuRenderRuleVo;
    }

    @Override
    public List<MenuPo> getSupportParent(Integer typeCode) {
        List<MenuPo> allMenuPo = this.getAll();
        for (MenuPo menuPo : allMenuPo) {
            boolean support = isSupport(menuPo, typeCode);
            menuPo.setDisabled(!support);
        }
        return packageSourceChildren(allMenuPo);
    }

    @Override
    public MenuPo getByClientUri(String clientUri) {
        if (ObjectUtil.isEmpty(clientUri)) return null;

        LambdaQueryWrapper<MenuPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MenuPo::getClientUri, clientUri);
        List<MenuPo> menuPos = menuMapper.selectList(wrapper);
        if (!ObjectUtil.isEmpty(menuPos)) {
            return menuPos.get(0);
        }
        return null;
    }

    @Override
    public List<MenuPo> getByTypeInAuth(MenuConstant.Type menuType) {
        List<MenuPo> menuPos = this.getInAuth();
        if (ObjectUtil.isEmpty(menuPos)) return null;
        return menuPos.stream().filter(menuPo -> menuPo.getType().equals(menuType.getCode()))
                .collect(Collectors.toList());
    }
}
